#include "Board.h"
#include <iostream>
#include <glut.h>

using namespace std;

Board board;
int score = 0, difficulty, cameraPosX, cameraPosY, cameraPosZ, lookAtX, lookAtY, lookAtZ, limit, status, startingSpeed;
char *text = (char*)malloc(28 * sizeof(char));
bool decrementedFlag = false;

void SetupLights()
{
    GLfloat mat_ambient[] = { 0.0f, 0.0f, 0.0, 1.0f };
    GLfloat mat_diffuse[] = { 0.1f, 0.6f, 0.1, 1.0f };
    GLfloat mat_specular[] = { 0.0f, 0.0f, 0.0, 1.0f };
    GLfloat light_position[] = { 1.0f, 1.0f, 1.0f, 0.0f };
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, mat_diffuse);
    glLightfv(GL_LIGHT0, GL_AMBIENT, mat_ambient);
    glLightfv(GL_LIGHT0, GL_SPECULAR, mat_specular);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
}

void ChangeLightColor(float red, float green, float blue)
{
    GLfloat mat_diffuse[] = { red, green, blue, 1.0f };
    glLightfv(GL_LIGHT0, GL_DIFFUSE, mat_diffuse);
}


void handleSpecialKeys(int key, int x, int y)
{
    switch (key)
    {   case GLUT_KEY_RIGHT: if (board.snake->direction != GO_DOWN)board.snake->direction = GO_UP; break;
        case GLUT_KEY_LEFT: if (board.snake->direction != GO_UP) board.snake->direction = GO_DOWN; break;
        case GLUT_KEY_UP: if (board.snake->direction != GO_RIGHT) board.snake->direction = GO_LEFT; break;
        case GLUT_KEY_DOWN:  if (board.snake->direction != GO_LEFT) board.snake->direction = GO_RIGHT; break;
    }
}

void drawBarriers()
{
    ChangeLightColor(1.0f,1.0f,1.0f);
    int x = -1 * (WINDOW_WIDTH / 2);
    int y = -1 * (WINDOW_HEIGHT / 2);    
    for (int j = 0; j <= (CELL_HEIGHT * 2) - 1; j++)
    {
        for (int i = 0; i <= (CELL_WIDTH * 2) - 1; i++)
        {
            for (int k = 0; k <= (CELL_HEIGHT * 2) + (CELL_WIDTH * 2) - 1; k++)
            {
                if (board.barriers[k].location.x == i && board.barriers[k].location.y == j)
                {
                    glPushMatrix();
                        glTranslatef(x, 0, y);
                        glScalef(CELL_SIZE, 20, CELL_SIZE);
                        glutSolidCube(1);
                    glPopMatrix();
                }
            }
            x += CELL_SIZE;
        }
        x = -1 * (WINDOW_WIDTH / 2);
        y += CELL_SIZE;
    }    
}

void drawFood()
{
    ChangeLightColor(1.0f, 0.0f, 0.0f);
    int x = -1 * (WINDOW_WIDTH / 2);
    int y = -1 * (WINDOW_HEIGHT / 2);
    for (int j = 0; j <= (CELL_HEIGHT * 2) - 1; j++)
    {
        for (int i = 0; i <= (CELL_WIDTH * 2) - 1; i++)
        {            
            if (board.food.location.x == i && board.food.location.y == j)
            {
                glPushMatrix();
                    glTranslatef(x, 0, y);
                    glScalef(CELL_SIZE, 20, CELL_SIZE);
                    glutSolidSphere(1, 20, 20);
                glPopMatrix();
            }
            x += CELL_SIZE;
        }
        x = -1 * (WINDOW_WIDTH / 2);
        y += CELL_SIZE;
    }
}

void drawSnake()
{
    ChangeLightColor(0.0f, 1.0f, 0.0f);
    int x = -1 * (WINDOW_WIDTH / 2);
    int y = -1 * (WINDOW_HEIGHT / 2);

    bool n = false;

    for (int j = 0; j <= (CELL_HEIGHT * 2) - 1; j++)
    {
        for (int i = 0; i <= (CELL_WIDTH * 2) - 1; i++)
        {
            for (int k = 0; k <= board.snake->segments.size() - 1; k++)
            {
                if (board.snake->segments[k].x == i && board.snake->segments[k].y == j)
                {
                    //This will let the camera follow the snake
                    /*if (!n)
                    {
                        glLoadIdentity();
                        cameraPosX = x - 400;
                        cameraPosY = 400;
                        cameraPosZ = y;
                        lookAtX = x;
                        lookAtY = 0;
                        lookAtZ = y;
                        gluLookAt(cameraPosX, cameraPosY, cameraPosZ, lookAtX, lookAtY, lookAtZ, 0, 1, 0);
                        n = true;
                    }*/

                    glPushMatrix();
                        glTranslatef(x, 0, y);
                        glScalef(CELL_SIZE, 20, CELL_SIZE);
                        glutSolidSphere(1, 20, 20);
                    glPopMatrix();
                }
            }
            x += CELL_SIZE;
        }
        x = -1 * (WINDOW_WIDTH / 2);
        y += CELL_SIZE;
    }
}

void gameOver()
{
    board.snake->isDead = true;
    glPushMatrix();
        glTranslatef(680.0, 400, 0.0);
        glScalef(0.25, 0.25, 1);
        if (status)
        {
            for (int i = 7; i <= 16; i++)
            {
                glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, *(text + i));
            }
        } else {
            for (int i = 17; i <= 27; i++)
            {
                glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, *(text + i));
            }
        }

    glPopMatrix();    
    glutPostRedisplay();
}

void drawScore()
{
    glPushMatrix();
        glTranslatef(0.0, 780, 0.0);
        glScalef(0.25, 0.25, 1);
        for (int i = 0; i <= 6; i++)
        {
            glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, *(text + i));
        }
        glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, 48 + ((score - (score - (score % 100)) - (score % 10)) / 10));
        glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, 48 + (score % 10));
    glPopMatrix();
}

void draw2D()
{
    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 1583, 0, 822);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_CULL_FACE);
    drawScore();
    if (score == limit)
    {
        status = 1; //Win
        gameOver();
    }
    if (board.snake->isDead && !status)
    {
        status = 0; //Lose
        gameOver();
    }
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, 1583 / 822, 0.1, 9000);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(cameraPosX, cameraPosY, cameraPosZ, lookAtX, lookAtY, lookAtZ, 0, 1, 0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_CULL_FACE);
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    draw2D();
    drawBarriers();
    drawFood();
    drawSnake();
    glFlush();
}

void loop(int arg)
{
    board.snake->update();
    if (board.snake->checkForFoodCollision() && score != limit)
    {
        score++;
    }
    if (score % difficulty == 0 && score!=0 && arg > 0 && !decrementedFlag)
    {
        arg -= 10;
        decrementedFlag = true;
    } else if (score % difficulty != 0) {
        decrementedFlag = false;
    }
    glutPostRedisplay();
    glutTimerFunc(arg, loop, arg);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(1583, 822);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Snake");
    glutDisplayFunc(display);
    glutSpecialFunc(handleSpecialKeys);
    glClearColor(0.0, 0.0, 0.0, 0.0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, 1583 / 822, 0.1, 9000);

    cameraPosX = 1000;
    cameraPosY = 1000;
    cameraPosZ = 1000;
    lookAtX = 0;
    lookAtY = 0;
    lookAtZ = 0;
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(cameraPosX, cameraPosY, cameraPosZ, lookAtX, lookAtY, lookAtZ, 0, 1, 0);

    SetupLights();
    glEnable(GL_NORMALIZE);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);

    text = "Score: YOU WIN!!!YOU LOSE!!!";
    difficulty = 2; //Increase to make it easier
    limit = 50;  // Score limit
    startingSpeed = 200; //Decrease to make the snake start with a slower pace
    board.initialize();

    glutTimerFunc(startingSpeed, loop, startingSpeed);
    glutMainLoop();
    return 0;
}